<template>
  <div class="fc-style">
    <el-form
      ref="generateForm"
      label-suffix=":"
      :model="model"
      :rules="rules"
      :size="widgetForm.config.size"
      :label-position="widgetForm.config.labelPosition"
      :label-width="`${widgetForm.config.labelWidth}px`"
      :hide-required-asterisk="widgetForm.config.hideRequiredAsterisk"
      :disabled="widgetForm.config.disabled"
    >
      <template v-for="(element, index) of widgetForm.list">
        <template v-if="element.type === 'grid'">
          <el-row
            type="flex"
            v-if="element.key"
            :key="element.key"
            :gutter="element.options.gutter ?? 0"
            :justify="element.options.justify"
            :align="element.options.align"
          >
            <el-col
              v-for="(col, colIndex) of element.columns"
              :key="colIndex"
              :span="col.span ?? 0"
            >
              <!-- eslint-disable-next-line vue/no-v-for-template-key -->
              <template v-for="colItem of col.list" :key="colItem.key">
                <template
                  v-if="colItem.type === 'slot' && colItem.display === true"
                >
                  <slot
                    :name="colItem.slotName"
                    v-bind="{ model, element: colItem }"
                  ></slot>
                </template>
                <ElGenerateFormItem
                  v-else-if="colItem.display === true"
                  :model="model"
                  :element="colItem"
                  :config="data.config"
                  :disabled="disabled"
                >
                </ElGenerateFormItem>
              </template>
            </el-col>
          </el-row>
        </template>
        <template
          v-else-if="element.type === 'slot' && element.display === true"
        >
          <slot :name="element.slotName" v-bind="{ model, element }"></slot>
        </template>
        <template v-else-if="element.display === true">
          <ElGenerateFormItem
            :model="model"
            :key="element.key"
            :element="widgetForm.list[index]"
            :config="data.config"
            :disabled="disabled"
          >
          </ElGenerateFormItem>
        </template>
      </template>
    </el-form>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, toRefs, watch } from 'vue'
import { ElMessage } from 'element-plus'
import ElGenerateFormItem from './ElGenerateFormItem.vue'
import { element } from '@/config'
import { cloneDeep, merge } from 'lodash'
import { getConfigByModel } from '@/utils'

// 渲染form
export default defineComponent({
  name: 'ElGenerateForm',
  components: {
    ElGenerateFormItem
  },
  props: {
    data: {
      type: Object,
      default: element.widgetForm
    },
    value: {
      type: Object
    },
    disabled: {
      type: Boolean,
      default: false
    },
    request: {
      type: Function
    }
  },
  emits: ['update:modelValue'],
  setup(props, context) {
    const state = reactive({
      generateForm: null as any,
      model: {} as any,
      rules: {} as any,
      widgetForm:
        (props.data && JSON.parse(JSON.stringify(props.data))) ??
        element.widgetForm
    })

    // const inputFields = ['input', 'password', 'textarea', 'text', 'richtext-editor', 'cascader']
    const selectFields = [
      'number',
      'radio',
      'checkbox',
      'time',
      'date',
      'rate',
      'select',
      'switch',
      'slider',
      'img-upload'
    ]
    const languageList = ['zh_CN', 'zh_HK', 'en_US']
    const tipsDict = {
      plEnter: ['请输入', '請輸入', 'Please enter'],
      plSelect: ['请选择', '請選擇', 'Please select']
    }
    const generateModel = (list: any[]) => {
      const localeLanguage =
        window.localStorage.getItem('localeLanguage') || 'zh_CN'
      const languageIndex = languageList.indexOf(localeLanguage)
      for (let index = 0; index < list.length; index++) {
        const model = list[index].model
        if (!model) {
          return
        }
        if (list[index].type === 'grid') {
          list[index].columns.forEach((col: any) => generateModel(col.list))
        } else {
          if (props.value && Object.keys(props.value).includes(model)) {
            state.model[model] = props.value[model]
          } else {
            state.model[model] = list[index].options.defaultValue
          }
          // 添加rules规则，并自动填充required message信息
          state.rules[model] = list[index].options.rules
          let requiredRules = {
            required: false,
            message: ''
          }
          if (Array.isArray(state.rules[model])) {
            requiredRules =
              state.rules[model].filter(
                (item: { required: boolean }) => item.required
              )[0] || {}
          } else if (state.rules[model]?.required) {
            requiredRules = state.rules[model]
          }
          if (requiredRules.required && !requiredRules.message) {
            let preTxt = tipsDict.plEnter
            if (selectFields.includes(list[index].type)) {
              preTxt = tipsDict.plSelect
            }
            requiredRules.message = `${preTxt[languageIndex]}${list[index].label}`
          }
        }
      }
    }

    const generateOptions = (list: any[]) => {
      list.forEach(async (item) => {
        if (item.type === 'grid') {
          item.columns.forEach((col: any) => generateOptions(col.list))
        } else {
          if (item.options && item.options.remote && item.options.remoteFunc) {
            try {
              if (props.request) {
                const json = await props.request(item.options.remoteFunc, item.options.remoteArg)
                if (json instanceof Array) {
                  item.options.remoteOptions = json.map((data) => ({
                    label: data[item.options.props.label],
                    value: data[item.options.props.value],
                    children: data[item.options.props.children]
                  }))
                }
              } else {
                console.error('请在初始化时传入请求模块')
              }
            } catch (error) {}
          }
        }
      })
    }

    const getConfig = (model: string) =>
      getConfigByModel(state.widgetForm.list, model)
    const setConfig = (model: string, options = {}) => {
      merge(getConfigByModel(state.widgetForm.list, model), options)
    }
    const remove = (model: string) => {
      delete state.model[model]
      getConfigByModel(state.widgetForm.list, model).display = false
    }
    const add = (model: string) => {
      getConfigByModel(state.widgetForm.list, model).display = true
    }
    const setOptions = (model: string, options = {}) => {
      const widget = getConfigByModel(state.widgetForm.list, model)
      if (widget.type === 'checkbox') state.model[model] = []
      widget.options.options = options
    }

    let initialModelValue = {}
    watch(
      () => props.data,
      (val) => {
        state.widgetForm =
          (val && JSON.parse(JSON.stringify(val))) ?? element.widgetForm
        state.model = {}
        state.rules = {}
        generateModel(state.widgetForm.list)
        initialModelValue = cloneDeep(state.model)
        generateOptions(state.widgetForm.list)
      },
      { deep: true, immediate: true }
    )

    watch(
      () => state.model,
      (val) => {
        context.emit('update:modelValue', val)
      },
      { deep: true, immediate: true }
    )

    const getData = () => {
      return new Promise((resolve, reject) => {
        state.generateForm
          .validate()
          .then((validate: boolean) => {
            if (validate) {
              resolve(state.model)
            } else {
              ElMessage.error('验证失败')
            }
          })
          .catch((error: Error) => {
            reject(error)
          })
      })
    }

    const reset = () => {
      state.generateForm.resetFields()
      state.model = cloneDeep(initialModelValue)
    }

    return {
      ...toRefs(state),
      getData,
      reset,
      getConfig,
      setConfig,
      remove,
      add,
      setOptions
    }
  }
})
</script>
